package essential;

import java.awt.Color;
import javax.vecmath.*;
import primitives.Renderable;

public class Ray {
	private Vector3d origin;
	private Vector3d direction;
	private double impactDist;
	public Vector3d impactNormal;
	public Color impactColor;
    private Renderable lastPrim = null;
    Type type;
    
    // Initial refractionIndex = Air Refraction Index = 1
    private double refractionIndex = 1;

	//Used for AABB intersection
	public enum Signs {
		MMM, MMP, MPM, MPP, PMM, PMP, PPM, PPP, NONE
	};
	
	public enum Type{
		Default,Shadow
	};
	
	public Signs signs;

	public Ray(){
		this.type = Type.Default;
		this.origin = new Vector3d();
		this.direction = new Vector3d();
		this.impactNormal = new Vector3d();
		this.impactColor = new Color(0);
		this.impactDist = Double.MAX_VALUE;
	}

	public Ray(Vector3d origin, Vector3d direction){
		this.type = Type.Default;
		if(origin != null)
		  this.origin = new Vector3d(origin);
		else
			this.origin = new Vector3d();
		
		if(direction != null)
			this.direction = new Vector3d(direction);
		else 
			this.direction = new Vector3d();
		
		this.impactDist = Double.MAX_VALUE;
		setSigns();
	}

	public Ray(Vector3d origin, Vector3d direction, Renderable lastPrim){
		this(origin, direction);
		this.lastPrim = lastPrim;
	}
	
	public Ray(Vector3d origin, Vector3d direction, Renderable lastPrim, double refractionIndex){
		this(origin, direction);
		this.lastPrim = lastPrim;
		this.refractionIndex = refractionIndex;
	}

	public Ray(Vector3d origin, Vector3d direction, double dist){
		this(origin,direction);
		this.impactDist = dist;
	}
	
	public Ray(Ray r) {
		this.type = r.type;
		this.origin = new Vector3d(r.origin);
		this.direction = new Vector3d(r.direction);
		this.impactDist = r.impactDist;
		this.impactNormal = new Vector3d(r.impactNormal);
		this.impactColor = new Color(r.impactColor.getRGB());
		this.lastPrim = r.lastPrim;
		this.signs = r.signs;
	}
	
	public Vector3d getIntersectionPoint(){
		Vector3d p = new Vector3d();
		
		if( impactDist == Double.MAX_VALUE )
			return p;
		
		Vector3d aux = new Vector3d(direction);
	    aux.scale(impactDist);
	    aux.add(origin);
	    p.set(aux.x,aux.y,aux.z);
		
	    setSigns();

		return  p;
	}

	public void setOrigin(Vector3d origin) {
		this.origin = origin;
	}
	
	public Vector3d getOrigin() {
		return origin;
	}
	
	public void setDirection(Vector3d direction) {
		this.direction = direction;
		setSigns();
	}

	public Vector3d getDirection() {
		return direction;
	}

	public void setImpactDist(double impactDist) {
		this.impactDist = impactDist;
	}

	public double getImpactDist() {
		return impactDist;
	}

	public void setLastRenderable(Renderable lastPrim) {
		this.lastPrim = lastPrim;
	}

	public Renderable getLastRenderable() {
		return lastPrim;
	}
	
	public Vector3d getSummarizedRay(){
		
		return new Vector3d( this.getIntersectionPoint());
		
	}
	
	public Vector3d getSummarizedRayWithOffset(){
		
		Vector3d v = this.getSummarizedRay();
		v.scale(1.001);
		
		return v;
		
	}
	
	public String toString() {
		return "Ray(" + origin + ", " + direction + ")";
	}

	void setSigns() {
		if (direction == null)
			signs = Signs.NONE;
		else if (direction.x < 0 && direction.y < 0 && direction.z < 0)
			signs = Signs.MMM;
		else if (direction.x < 0 && direction.y < 0 && direction.z >= 0)
			signs = Signs.MMP;
		else if (direction.x < 0 && direction.y >= 0 && direction.z < 0)
			signs = Signs.MPM;
		else if (direction.x < 0 && direction.y >= 0 && direction.z >= 0)
			signs = Signs.MPP;
		else if (direction.x >= 0 && direction.y < 0 && direction.z < 0)
			signs = Signs.PMM;
		else if (direction.x >= 0 && direction.y < 0 && direction.z >= 0)
			signs = Signs.PMP;
		else if (direction.x >= 0 && direction.y >= 0 && direction.z < 0)
			signs = Signs.PPM;
		else if (direction.x >= 0 && direction.y >= 0 && direction.z >= 0)
			signs = Signs.PPP;
	}

	public void setRIndex(double refractionIndex) {
		this.refractionIndex = refractionIndex;
	}

	public double getRIndex() {
		return refractionIndex;
	}
}
